#pragma once

#include <math.h>

#define V2D Vector2D<TYPE>
#define V2DI Vector2D<int>
#define V2DF Vector2D<float>
#define V2D_ZERO V2DF(0,0)
#define V2D_ONES V2DF(1,1)
#define V2D_DOUBLE V2DF(2,2)

template <typename TYPE>
struct Vector2D
{
	TYPE x, y;

	Vector2D(TYPE a_x = 0, TYPE a_y = 0)
		:x(a_x), y(a_y){}

	//operator overload
	TYPE operator[](const int& n)
	{return (n==0)?x:(n==1)?y:0;}

	bool operator<(const V2D& n)
	{return (x<n.x&&y<n.y)?true:false;}
	bool operator>(const V2D& n)
	{return (x>n.x&&y>n.y)?true:false;}

	V2D operator+(const V2D& n)
	{return Vector2D(x+n.x,y+n.y);}
	V2D operator-(const V2D& n)
	{return Vector2D(x-n.x,y-n.y);}
	V2D operator*(const V2D& n)
	{return Vector2D(x*n.x,y*n.y);}
	V2D operator/(const V2D& n)
	{return Vector2D(x/n.x,y/n.y);}

	void operator+=(const V2D& v)
	{x += v.x; y += v.y;}
	void operator-=(const V2D& v)
	{x -= v.x; y -= v.y;}
	void operator*=(const V2D& v)
	{x *= v.x; y *= v.y;}
	void operator/=(const V2D& v)
	{x /= v.x; y /= v.y;}

	V2D operator+(const TYPE& n)
	{return Vector2D(x+n,y+n);}
	V2D operator-(const TYPE& n)
	{return Vector2D(x-n,y-n);}
	V2D operator*(const TYPE& n)
	{return Vector2D(x*n,y*n);}
	V2D operator/(const TYPE& n)
	{return Vector2D(x/n,y/n);}

	void operator+=(const TYPE& n)
	{x += n; y += n;}
	void operator-=(const TYPE& n)
	{x -= n; y -= n;}
	void operator*=(const TYPE& n)
	{x *= n; y *= n;}
	void operator/=(const TYPE& n)
	{x /= n; y /= n;}

	bool operator==(const V2D& v)
	{return (x == v.x && y == v.y);}
	bool operator!=(const V2D& v)
	{return (x != v.x && y != v.y);}

	//setter functions
	void set(TYPE nx, TYPE ny)
	{x=nx;y=ny;}

	void set(V2D v)
	{x=v.x;y=v.y;}

	void limit(TYPE n)
	{
		if(magnitude() > n)
			*this = normal()*n;
	}

	//bool functions
	bool isZero()
	{return (x == 0 && y == 0);}

	//void functions
	void zero()
	{x = 0; y = 0;}

	//class functions
	V2D normalize()
	{
		float mag = magnitude();
		if(x != 0)
			x /= mag;
		if(y != 0)
			y /= mag;
		return *this;
	}

	V2D normal()
	{V2DF norm(*this); return norm.normalize();}

	V2D perp()
	{return V2DF(-y,x);}

	//scalar functions
	TYPE magnitudeSq()
	{return x*x+y*y;}

	TYPE magnitude()
	{return sqrt(magnitudeSq());}

	TYPE distanceSq(V2D v)
	{TYPE dx = x-v.x, dy = y-v.y; return dx*dx+dy*dy;}

	TYPE distance(V2D v)
	{return sqrt(distanceSq(v));}

	TYPE dot(V2D v)
	{return (x*v.x + y*v.y);}

	TYPE cross(V2D v)
	{return (x*v.y - v.x*y);}
};